###############################################################################
#
# file: EditAttrDialog.py
# author: Victor Bos
# 
# Copyright 2007--2011 Victor Bos
#
# This file is part of GREATOR.
#
#    GREATOR is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    GREATOR is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with GREATOR.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import copy
import ScrolledText
import StringIO
import Tkconstants
import Tkinter

import io

FG_BORDER_WIDTH=5

CANCEL = 0
DELETE = 1
OK = 2

class EditAttrDialog(Tkinter.Toplevel):


    def __init__(self, master=None, **kw):

        self.process_options(kw)
        Tkinter.Toplevel.__init__(self, master, kw)
        self.protocol('WM_DELETE_WINDOW', self.cancel_pressed)
        self.title(self.windowtitle)

        self.attrs = {}

        lframe = Tkinter.LabelFrame(
            self, 
            text=self.message)
        lframe.pack(side = Tkinter.TOP, fill=Tkinter.BOTH, expand=1)

        self.create_main_frame(lframe)

        self.create_ok_cancel_buttons()

        self.result = CANCEL
        self.center_on_master(master)

    def clear_attr_text(self):
        if self.attr_text is not None:
            self.attr_text.delete("1.0",Tkinter.END)

    def append_attr_text(self, s):
        self.attr_text.insert(Tkinter.END, s)

    def set_attrs(self, attrs, init=False):
        # delete keys+values that are not in attrs
        # and update the result flag if there is a change.
        for a in self.attrs.keys():
            if a not in attrs:
                del self.attrs[a]
        for a in attrs:
            # add the new keys+values from attrs
            if a not in self.attrs:
                self.attrs[a] = None
            if attrs[a] != self.attrs[a]:
                # set the correct values
                self.attrs[a] = attrs[a]

    def init_attrs(self, attrs):
        self.set_attrs(attrs, init = True)

    def show_attrs(self):
        self.clear_attr_text()
        self.append_attr_text(io.attrs_to_str(self.attrs))

    def create_main_frame(self, lframe):
        f = Tkinter.Frame(lframe)
        self.attr_text = ScrolledText.ScrolledText(
            f,
            bd=FG_BORDER_WIDTH,
            width=1,
            height=8,
            background="ghostwhite")
        self.attr_text.pack(side = Tkinter.TOP, 
                            anchor = Tkinter.SW, 
                            fill = Tkinter.BOTH, 
                            expand = 1)

        f.pack(side = Tkinter.TOP, 
               expand = 1, 
               fill = Tkinter.BOTH, 
               padx = 10, 
               pady = 10)
    
    def read_attrs(self):
        self.buffer = []
        self.attr_text.dump(
            "1.0",
            Tkinter.END,
            command=self.filter_text,
            text=True)
        attrs_str = ("".join(self.buffer)).strip()
        attrs = io.read_attrs(attrs_str)
        return attrs

    def update_attrs(self):
        attrs = self.read_attrs()
        self.set_attrs(attrs)

    def filter_text(self, k, v, i):
        self.buffer.append(v)

    def get_attrs(self):
        return self.attrs

    def get_attrs_str(self, spc=""):
        return io.attrs_to_str(self.get_attrs(), spc=spc)

    def create_ok_cancel_buttons(self):
        f = Tkinter.Frame(self)

        self.delete_button = Tkinter.Button(
            f,
            text="Delete",
            command=self.delete_pressed)
        self.delete_button.pack(side=Tkinter.LEFT)

        Tkinter.Label(f).pack(side=Tkinter.LEFT, expand=1)

        self.ok_button = Tkinter.Button(
            f,
            text="Ok",
            command=self.ok_pressed)
        self.ok_button.pack(side=Tkinter.LEFT)

        self.cancel_button = Tkinter.Button(
            f,
            text="Cancel",
            command=self.cancel_pressed)
        self.cancel_button.pack(side=Tkinter.LEFT)
        f.pack(side = Tkinter.TOP, 
               anchor = Tkinter.SW, 
               fill = Tkinter.X, 
               expand = 1)
    
    def center_on_master(self, master):
        if master is not None:
            x = (master.winfo_rootx() +
                 ((master.winfo_width() - self.winfo_reqwidth()) / 2.0))
            y = (master.winfo_rooty() +
                 ((master.winfo_height() - self.winfo_reqheight()) / 2.0))
            self.geometry("+%d+%d" % (x,y))
        
    def process_options(self, kw):
        self.set_option("message", kw, "Edit attributes")
        self.set_option("windowtitle", kw, "Attribute editor")
        
    def set_option(self, name, user_values, default):
        if name in user_values:
            setattr(self, name, user_values[name])
            del user_values[name]
        else:
            setattr(self, name, default)

    def delete_pressed(self):
        self.result = DELETE
        self.quit()

    def ok_pressed(self):
        self.update_attrs()
        self.result = OK
        self.quit()

    def cancel_pressed(self):
        self.result = CANCEL
        self.quit()

    def go(self):
        self.wait_visibility() # window needs to be visible for the grab
        self.grab_set()
        self.show_attrs()
        self.mainloop()
        return self.result

class EditAreaDialog(EditAttrDialog):

    def process_options(self, kw):
        self.set_option("message", kw, "Area")
        self.set_option("windowtitle", kw, "Area editor")

    def create_main_frame(self, lframe):

        self.attrs['name'] = "Unknown Area"
        self.area_name = Tkinter.StringVar()
        self.name_entry = Tkinter.Entry(lframe,
                                        textvariable = self.area_name)
        self.name_entry.pack(side = Tkinter.TOP,
                             expand = 1, 
                             fill = Tkinter.X)
 
        EditAttrDialog.create_main_frame(self, lframe)

    def init_attrs(self, attrs):
        if 'name' not in attrs:
            attrs = copy.copy(attrs)
            attrs['name'] = self.attrs['name']
        EditAttrDialog.init_attrs(self, attrs)
        self.area_name.set(self.attrs['name'])

    def show_attrs(self):
        attrs = copy.copy(self.attrs)
        del attrs['name']
        self.clear_attr_text()
        self.append_attr_text(io.attrs_to_str(attrs))
        
    def update_attrs(self):
        attrs = self.read_attrs()
        attrs['name'] = self.area_name.get()
        self.set_attrs(attrs)

if __name__ == "__main__":

    import sys

    dialog_names = ["base"]
    if len(sys.argv)>1:
        dialog_names = sys.argv[1:]

    root = Tkinter.Tk()
    root.title('Testing EditAttrDialog')

    def show_dialog(d):
        result = d.go()
        if result == CANCEL:
            pass
        elif result == DELETE:
            pass
        else:
            attrs = d.get_attrs()
        root.quit()

    def show_dialogs(d_names = dialog_names):
        for dn in d_names:
            attrs = { 'abc' : 'value 1', 
                      'def' : 'value 2',
                      'ghi' : 'value 3' }
            ed = None
            if dn == 'base':
                ed = EditAttrDialog(root)
                ed.init_attrs(attrs)
            elif dn == 'area':
                ed = EditAreaDialog(root)
                ed.init_attrs(attrs)
            if ed is not None:
                show_dialog(ed)

    button = Tkinter.Button(root, text='show dialog', command = show_dialogs)
    button.pack(side = Tkinter.TOP)

    root.mainloop()


